/*
Copyright 2011-2016 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.google.security.zynamics.zylib.gui.dndtree;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.swing.JComponent;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
public abstract class AbstractTreeTransferHandler implements DragGestureListener,
DragSourceListener, DropTargetListener {
private static DefaultMutableTreeNode draggedNode;
private static BufferedImage image = null; // buff image
private final DNDTree tree;
private final DragSource dragSource; // dragsource
private final DropTarget dropTarget; // droptarget
private DefaultMutableTreeNode draggedNodeParent;
private final Rectangle rect2D = new Rectangle();
private final boolean drawImage;
protected AbstractTreeTransferHandler(final DNDTree tree, final int action, final boolean drawIcon) {
this.tree = tree;
drawImage = drawIcon;
dragSource = new DragSource();
dragSource.createDefaultDragGestureRecognizer(tree, action, this);
dropTarget = new DropTarget(tree, action, this);
}
private final void clearImage() {
tree.paintImmediately(rect2D.getBounds());
}
private final void paintImage(final Point pt) {
if (image != null) {
tree.paintImmediately(rect2D.getBounds());
rect2D.setRect((int) pt.getX(), (int) pt.getY(), image.getWidth(), image.getHeight());
tree.getGraphics().drawImage(image, (int) pt.getX(), (int) pt.getY(), tree);
}
}
protected abstract boolean canPerformAction(DNDTree tree, DataFlavor flavor,
Transferable tranferable, int action, Point pt);
protected abstract boolean executeDrop(DNDTree tree, Transferable transferable,
DefaultMutableTreeNode newParentNode, int action);
public abstract boolean canPerformAction(DNDTree target, DefaultMutableTreeNode draggedNode,
int action, Point location);
/* Methods for DragSourceListener */
@Override
public void dragDropEnd(final DragSourceDropEvent dsde) {
if (dsde.getDropSuccess() && (dsde.getDropAction() == DnDConstants.ACTION_MOVE)
&& (draggedNodeParent != null)) {
((DefaultTreeModel) tree.getModel()).nodeStructureChanged(draggedNodeParent);
}
}
/* Methods for DropTargetListener */
@Override
public final void dragEnter(final DragSourceDragEvent dsde) {
final int action = dsde.getDropAction();
if (action == DnDConstants.ACTION_COPY) {
dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
} else {
if (action == DnDConstants.ACTION_MOVE) {
dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
} else {
dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
}
}
}
@Override
public final void dragEnter(final DropTargetDragEvent dtde) {
final Point pt = dtde.getLocation();
final int action = dtde.getDropAction();
if (drawImage) {
paintImage(pt);
}
final Transferable transferable = dtde.getTransferable();
if (!transferable.isDataFlavorSupported(TransferableNode.NODE_FLAVOR)) {
if (canPerformAction(tree, dtde.getCurrentDataFlavorsAsList().get(0), dtde.getTransferable(),
action, pt)) {
dtde.acceptDrag(action);
} else {
dtde.rejectDrag();
}
} else {
if (canPerformAction(tree, draggedNode, action, pt)) {
dtde.acceptDrag(action);
} else {
dtde.rejectDrag();
}
}
}
@Override
public final void dragExit(final DragSourceEvent dse) {
dse.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
}
@Override
public final void dragExit(final DropTargetEvent dte) {
if (drawImage) {
clearImage();
}
}
/* Methods for DragGestureListener */
@Override
public final void dragGestureRecognized(final DragGestureEvent dge) {
// final TreePath path = tree.getSelectionPath();
final TreePath path = tree.getPathForLocation(dge.getDragOrigin().x, dge.getDragOrigin().y);
if (path != null) {
draggedNode = (DefaultMutableTreeNode) path.getLastPathComponent();
draggedNodeParent = (DefaultMutableTreeNode) draggedNode.getParent();
if (drawImage) {
final Rectangle pathBounds = tree.getPathBounds(path); // getpathbounds of selectionpath
final JComponent lbl =
(JComponent) tree.getCellRenderer().getTreeCellRendererComponent(tree, draggedNode,
false, tree.isExpanded(path),
((DefaultTreeModel) tree.getModel()).isLeaf(path.getLastPathComponent()), 0, false);// returning
// the
// label
lbl.setBounds(pathBounds);// setting bounds to lbl
image =
new BufferedImage(lbl.getWidth(), lbl.getHeight(),
java.awt.image.BufferedImage.TYPE_INT_ARGB_PRE);// buffered image reference passing
// the label's ht and width
final Graphics2D graphics = image.createGraphics();// creating the graphics for buffered
// image
graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); // Sets
// the
// Composite
// for the
// Graphics2D
// context
lbl.setOpaque(false);
lbl.paint(graphics); // painting the graphics to label
graphics.dispose();
}
dragSource.startDrag(dge, DragSource.DefaultMoveNoDrop, image, new Point(0, 0),
new TransferableNode(draggedNode), this);
}
}
@Override
public final void dragOver(final DragSourceDragEvent dsde) {
final int action = dsde.getDropAction();
if (action == DnDConstants.ACTION_COPY) {
dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
} else {
if (action == DnDConstants.ACTION_MOVE) {
dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
} else {
dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
}
}
}
@Override
public final void dragOver(final DropTargetDragEvent dtde) {
final Point pt = dtde.getLocation();
final int action = dtde.getDropAction();
tree.autoscroll(pt);
if (drawImage) {
paintImage(pt);
}
final Transferable transferable = dtde.getTransferable();
if (!transferable.isDataFlavorSupported(TransferableNode.NODE_FLAVOR)) {
if (canPerformAction(tree, dtde.getCurrentDataFlavorsAsList().get(0), dtde.getTransferable(),
action, pt)) {
dtde.acceptDrag(action);
} else {
dtde.rejectDrag();
}
} else {
if (canPerformAction(tree, draggedNode, action, pt)) {
dtde.acceptDrag(action);
} else {
dtde.rejectDrag();
}
}
}
@Override
public final void drop(final DropTargetDropEvent dtde) {
if (drawImage) {
clearImage();
}
final int action = dtde.getDropAction();
final Transferable transferable = dtde.getTransferable();
final Point pt = dtde.getLocation();
if (transferable.isDataFlavorSupported(TransferableNode.NODE_FLAVOR)
&& canPerformAction(tree, draggedNode, action, pt)) {
boolean gotData = false;
DefaultMutableTreeNode node = null;
try {
node = (DefaultMutableTreeNode) transferable.getTransferData(TransferableNode.NODE_FLAVOR);
gotData = true;
} catch (final IOException e) {
// TODO: This should be properly logged
System.out.println(e);
} catch (final UnsupportedFlavorException e) {
// TODO: This should be properly logged
System.out.println(e);
}
if (gotData) {
final TreePath pathTarget = tree.getPathForLocation(pt.x, pt.y);
final DefaultMutableTreeNode newParentNode =
(DefaultMutableTreeNode) pathTarget.getLastPathComponent();
if (executeDrop(tree, node, newParentNode, action)) {
dtde.acceptDrop(action);
dtde.dropComplete(true);
return;
}
}
} else if (canPerformAction(tree, dtde.getCurrentDataFlavors()[0], dtde.getTransferable(),
action, pt)) {
final TreePath pathTarget = tree.getPathForLocation(pt.x, pt.y);
final DefaultMutableTreeNode newParentNode =
(DefaultMutableTreeNode) pathTarget.getLastPathComponent();
if (executeDrop(tree, dtde.getTransferable(), newParentNode, action)) {
dtde.acceptDrop(action);
dtde.dropComplete(true);
return;
}
}
dtde.rejectDrop();
dtde.dropComplete(false);
}
@Override
public final void dropActionChanged(final DragSourceDragEvent dsde) {
final int action = dsde.getDropAction();
if (action == DnDConstants.ACTION_COPY) {
dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
} else {
if (action == DnDConstants.ACTION_MOVE) {
dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
} else {
dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
}
}
}
@Override
public final void dropActionChanged(final DropTargetDragEvent dtde) {
final Point pt = dtde.getLocation();
final int action = dtde.getDropAction();
if (drawImage) {
paintImage(pt);
}
if (draggedNode == null) {
if (canPerformAction(tree, dtde.getCurrentDataFlavorsAsList().get(0), dtde.getTransferable(),
action, pt)) {
dtde.acceptDrag(action);
} else {
dtde.rejectDrag();
}
} else {
if (canPerformAction(tree, draggedNode, action, pt)) {
dtde.acceptDrag(action);
} else {
dtde.rejectDrag();
}
}
}
public abstract boolean executeDrop(DNDTree tree, DefaultMutableTreeNode draggedNode,
DefaultMutableTreeNode newParentNode, int action);
public DropTarget getDropTarget() {
return dropTarget;
}
}